/**
 * Description 调度中心的方法
 * @date 2022/9/9 - 04:39:45
 *
 * @export
 * @interface Handlers
 * @typedef {Handlers}
 */
export declare interface Handlers {
  [key: string]: Array<Function>;
}

/**
 * 调度中心接口
 * @date 2022/9/9 - 04:40:23
 *
 * @export
 * @interface IMessageCenter
 * @typedef {IMessageCenter}
 */
export declare interface IMessageCenter {
  events: Handlers;
  _instance?: IMessageCenter;
  on: (type: string, handler: Function) => this;
  emit: (type: string, data?: any) => this;
  un: (type: string, handler?: Function) => this;
  once: (type: string, handler: Function) => this;
  clear: () => this;
  has: (type: string) => boolean;
  handlerLength: (type: string) => number;
  watch: (type: string, handler: Function) => this;
  invoke: (type: string, data?: any) => Promise<void>;
}

class MessageCenter implements IMessageCenter {
  events: Handlers = {};
  _instance?: IMessageCenter;
  /**
   * 注册事件至调度中心
   * @param type 事件类型，特指具体事件名
   * @param handler 事件注册的回调
   */
  on(type: string, handler: Function) {
    //订阅者
    this.checkHandler(type, handler);
    if (!this.has(type)) {
      //若调度中心未找到该事件的队列，则新建某个事件列表（可以对某个类型的事件注册多个回调函数）
      this.events[type] = [];
    }
    this.events[type].push(handler);
    return this;
  }
  /**
   * 触发调度中心的某个或者某些该事件类型下注册的函数
   * @param type 事件类型，特指具体事件名
   * @param data 发布者传递的参数
   */
  emit(type: string, data: any) {
    //发布者
    if (this.has(type)) {
      this.runHandler(type, data);
    }
    return this;
  }
  //销毁监听
  un(type: any, handler: (...args: any[]) => any) {
    this.unHandler(type, handler);
    return this;
  }
  // 只注册一次监听，执行即销毁
  once(type: string, handler: Function) {
    this.checkHandler(type, handler);
    const fn = (...args: any[]) => {
      this.un(type, fn);
      return handler(...args);
    };
    this.on(type, fn);
    return this;
  }
  // 重置调度中心
  clear() {
    this.events = {};
    return this;
  }
  // 判断事件是否被订阅
  has(type: string) {
    return !!this.events[type];
  }
  // 同一个事件被绑定了多少函数
  handlerLength(type: string) {
    return this.events[type]?.length ?? 0;
  }
  // 监听invoke的消息，若handler中进行了计算或者异步操作，会反馈给invoke
  watch(type: string, handler: Function) {
    this.checkHandler(type, handler);
    const fn = (...args: any[]) => {
      this.emit(this.prefixStr(type), handler(...args));
    };
    this.on(type, fn);
    return this;
  }
  // 触发watch事件，并且接收watch处理结果
  invoke(type: any, data: any) {
    return new Promise<void>(resolve => {
      this.once(this.prefixStr(type), resolve);
      this.emit(type, data);
    });
  }
  // 批量执行调度中心中某个函数集
  private runHandler(type: string | number, data: any) {
    for (let i = 0; i < this.events[type].length; i++) {
      this.events[type][i] && this.events[type][i](data);
    }
  }
  // 批量销毁调度中心中某个函数集
  private unHandler(type: string, handler: Function) {
    !handler && (this.events[type] = []);
    handler && this.checkHandler(type, handler);
    for (let i = 0; i < this.events[type].length; i++) {
      if (this.events[type][i] && this.events[type][i] === handler) {
        this.events[type][i] = null;
      }
    }
  }
  private prefixStr(str: any) {
    return `@${str}`;
  }
  /**
   * 检查参数是否符合标准
   * @param type 事件名
   * @param handler 事件钩子
   */
  private checkHandler(type: string, handler: Function) {
    if (type?.length === 0) {
      throw new Error('type.length can not be 0');
    }
    if (!handler || !type) {
      throw new ReferenceError('type or handler is not defined');
    }
    if (typeof handler !== 'function' || typeof type !== 'string') {
      throw new TypeError(`${handler} is not a function or ${type} is not a string`);
    }
  }
}

export const messageCenter = new MessageCenter();
export const decoratorMessageCenter: ClassDecorator = <TFunction extends Function>(
  target: TFunction
) => {
  if (!target.prototype.messageCenter) {
    target.prototype.messageCenter = new MessageCenter();
  }
  return target;
};
export default messageCenter;
